/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName : fs_linux.c
*  Author : rivulet.dave
*  Description : Linux文件系统接口
*  Data : 2020-07-20
******************************************************/

/*********************************************************************
 * INCLUDES
 */

#include "stdrope.h"
#include "sallib.h"
#include "salagent.h"


#if (SAL_FS == _ENABLE && SAL_FS_PRODUCER == _FS_LINUX_)

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>

#include "log.h"
#include "fs.h"
#include "fs_linux.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

static bool s_bIsLogEnable = true;
#define FS_LOG(level, ...) do{if (s_bIsLogEnable) LOG_Output("MOD_FS", level, 0,  __FILENAME__, __LINE__, ##__VA_ARGS__);}while(0)

/*********************************************************************
 * TYPES
 */

/*********************************************************************
 * FUNCTIONS DECLARE
 */

static FRESULT_E fsOpenByLinux(fd_t *phFile, const char* pcPath, uint8_t ucMode);
static FRESULT_E fsCloseByLinux(fd_t *phFile);
static int32_t   fsReadByLinux(fd_t *phFile, void *pBuff, uint32_t uiSize);
static int32_t   fsReadLineByLinux(fd_t *phFile, void *pBuff, uint32_t uiSize);
static int32_t   fsWriteByLinux(fd_t *phFile, const void *pBuff, uint32_t uiSize);
static FRESULT_E fsSeekByLinux(fd_t *phFile, int32_t iOffset, uint8_t ucWhence);
static FRESULT_E fsDeleteByLinux(const char *pName);
static FRESULT_E fsGetSizeByLinux(fd_t *phFile, uint32_t *puiSize);
static FRESULT_E fsFlushByLinux(fd_t *phFile);
static FRESULT_E fsRenameByLinux(const char *pOldPath, const char *pNewPath);
static FRESULT_E fsMkDirByLinux(const char *pPath);
static FRESULT_E fsDirOpenByLinux(const char *pcDirPath, FSDIR_S *pstDir);
static FRESULT_E fsDirCloseByLinux(FSDIR_S *pstDir);
static FRESULT_E fsDirReadByLinux(FSDIR_S *pstDir, FILE_INFO_S *pstFileInfo);
static char *    fsGetCwdByLinux(char *pBuf, uint32_t uiSize);
static FRESULT_E fsChDirByLinux(const char *pPath);
static FRESULT_E fsIsExistByLinux(const char *pPath);
static FRESULT_E fsChModByLinux(const char *pPath, uint32_t uiMode);

/*********************************************************************
 *  VARIABLES
 */

static FS_BASE_S s_stFSBaseLinuxObject;

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: Linux文件系统初始化
 *   @param : NA
 *   @return: 文件系统对象
 */
FS_BASE_S *fsLinuxObjNew(void)
{
    s_stFSBaseLinuxObject.pfFSOpen = fsOpenByLinux;
    s_stFSBaseLinuxObject.pfFSClose = fsCloseByLinux;
    s_stFSBaseLinuxObject.pfFSRead = fsReadByLinux;
    s_stFSBaseLinuxObject.pfFSReadLine = fsReadLineByLinux;
    s_stFSBaseLinuxObject.pfFSWrite = fsWriteByLinux;
    s_stFSBaseLinuxObject.pfFSSeek = fsSeekByLinux;
    s_stFSBaseLinuxObject.pfFSDelete = fsDeleteByLinux;
    s_stFSBaseLinuxObject.pfFSGetSize = fsGetSizeByLinux;
    s_stFSBaseLinuxObject.pfFSFlush = fsFlushByLinux;
    s_stFSBaseLinuxObject.pfFSRename = fsRenameByLinux;
    s_stFSBaseLinuxObject.pfFSMkDir = fsMkDirByLinux;
    s_stFSBaseLinuxObject.pfFSDirOpen = fsDirOpenByLinux;
    s_stFSBaseLinuxObject.pfFSDirClose = fsDirCloseByLinux;
    s_stFSBaseLinuxObject.pfFSDirRead = fsDirReadByLinux;
    s_stFSBaseLinuxObject.pfFSGetCwd = fsGetCwdByLinux;
    s_stFSBaseLinuxObject.pfFSChDir = fsChDirByLinux;
    s_stFSBaseLinuxObject.pfFSIsExist = fsIsExistByLinux;
    s_stFSBaseLinuxObject.pfFSChMod = fsChModByLinux;

    return &s_stFSBaseLinuxObject;
}

/**
 *   @Description: Linux文件系统对象删除
 *   @param : NA
 *   @return: NA
 */
void fsLinuxObjDelete(void)
{
}

static FRESULT_E fsOpenByLinux(fd_t *phFile, const char* pcPath, uint8_t ucMode)
{
    int32_t iHandle;

    iHandle = open(pcPath, ucMode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (0 > iHandle)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file open fail:%s.\n", strerror(errno));
        return FS_OPEN_ERR;
    }

    *phFile = (fd_t)iHandle;
    return FS_OK;
}

static FRESULT_E fsCloseByLinux(fd_t *phFile)
{
    int32_t hFile = *phFile;
    if (0 > close(hFile))
    {
        FS_LOG(LOG_LEVEL_ERROR, "file close fail.\n");
    }
    return FS_OK;
}

static int32_t   fsReadByLinux(fd_t *phFile, void *pBuff, uint32_t uiSize)
{
    int32_t iReadBytes;
    int32_t hFile = *phFile;

    if (NULL == pBuff)
    {
        return FS_INVALID_PARAMETER;
    }
    
    iReadBytes = read(hFile, pBuff, uiSize);
    if (iReadBytes == -1)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file read fail.\n");
        return FS_READ_ERR;
    }
    
    return iReadBytes;
}

static int32_t   fsReadLineByLinux(fd_t *phFile, void *pBuff, uint32_t uiSize)
{
    int32_t iReadCount;
    char cReadBuffer;
    int32_t iTotalCount = 0;
    char *pcBuffer = (char *)pBuff;
    int32_t hFile = *phFile;

    while ((iReadCount = read(hFile, &cReadBuffer, 1)) > 0)
    {
        pcBuffer[iTotalCount++] = cReadBuffer;
        if(iTotalCount == uiSize - 1)
        {
            break;
        }
        if(cReadBuffer == '\n' || cReadBuffer == EOF)
        {
            break;
        }

    }

    pcBuffer[iTotalCount] = '\0';
    return iTotalCount;
}

static int32_t   fsWriteByLinux(fd_t *phFile, const void *pBuff, uint32_t uiSize)
{
    int32_t iWriteBytes = 0;
    int32_t hFile = *phFile;

    if (NULL == pBuff)
    {
        return FS_INVALID_PARAMETER;
    }
    
    iWriteBytes = write(hFile, pBuff, uiSize);
    if (iWriteBytes < uiSize)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file write fail.\n");
        return FS_WRITE_ERR;
    }
    
    return iWriteBytes;
}

static FRESULT_E fsSeekByLinux(fd_t *phFile, int32_t iOffset, uint8_t ucWhence)
{
    int32_t iRet;
    int32_t hFile = *phFile;
    
    iRet = lseek(hFile, iOffset, ucWhence);
    if (0 > iRet)
    {
        FS_LOG(LOG_LEVEL_ERROR, "file seek fail.\n");
    }
    
    return FS_OK;
}

static FRESULT_E fsDeleteByLinux(const char *pName)
{
    if (0 > remove(pName))
    {
        FS_LOG(LOG_LEVEL_ERROR, "file delete fail.\n");
    }
    return FS_OK;
}

static FRESULT_E fsGetSizeByLinux(fd_t *phFile, uint32_t *puiSize)
{
    struct stat stbuf;
    int32_t hFile = *phFile;
    
    if ((fstat(hFile, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode)))
    {
        /* Handle error */
        return FS_NO_FILE;
    }
    
    *puiSize = stbuf.st_size;
    return FS_OK;
}

static FRESULT_E fsFlushByLinux(fd_t *phFile)
{
    int32_t hFile = *phFile;

    fsync(hFile);
    return FS_OK;
}

static FRESULT_E fsRenameByLinux(const char *pOldPath, const char *pNewPath)
{
    rename(pOldPath, pNewPath);
    return FS_OK;
}

static FRESULT_E fsMkDirByLinux(const char *pPath)
{
    mkdir(pPath, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    return FS_OK;
}

static FRESULT_E fsDirOpenByLinux(const char *pcDirPath, FSDIR_S *pstDir)
{
    DIR *pstTempDir = NULL;

    if (!pstDir)
    {
        return FS_INVALID_PARAMETER;
    }

    pstTempDir = opendir(pcDirPath);
    memcpy(pstDir, pstTempDir, sizeof(struct dirent));
    return FS_OK;
}

static FRESULT_E fsDirCloseByLinux(FSDIR_S *pstDir)
{
    closedir((DIR *)pstDir);
    return FS_OK;
}

static FRESULT_E fsDirReadByLinux(FSDIR_S *pstDir, FILE_INFO_S *pstFileInfo)
{
    struct dirent *pstFileName = NULL;
    
    pstFileName = readdir((DIR *)pstDir);
    if (pstFileName)
    {
        if (pstFileInfo)
        {
            strcpy(pstFileInfo->acFileName, pstFileName->d_name);
        }
        return FS_OK;
    }

    return FS_NO_FILE;
}

static char *    fsGetCwdByLinux(char *pBuf, uint32_t uiSize)
{
    return getcwd(pBuf, uiSize);
}

static FRESULT_E fsChDirByLinux(const char *pPath)
{
    if (0 > chdir(pPath))
    {
        if (EACCES  == errno)
        {
            return FS_DENIED;
        }
        else if (ENAMETOOLONG == errno || EFAULT == errno)
        {
            return FS_INVALID_NAME;
        }
        else if (ENOTDIR == errno)
        {
            return FS_NO_PATH;
        }
        else
        {
            return FS_OTHER_ERR;
        }
    }
    return FS_OK;
}

static FRESULT_E fsIsExistByLinux(const char *pPath)
{
    struct stat stFileStat;
    if (!stat(pPath, &stFileStat))
    {
        return FS_OK;
    }

    return FS_NO_FILE;
}

static FRESULT_E fsChModByLinux(const char *pPath, uint32_t uiMode)
{
    chmod(pPath, uiMode);
    return FS_OK;
}

#endif  // SAL_FS == _ENABLE

/************************************** The End Of File **************************************/

